#include "../build/genericFactory/soapH.h"
#include "../build/genericFactory/GenericFactory.nsmap"
#include "libservice.h"
#include <unistd.h>

char *serviceName = "GenericFactory";
char * uuid;

char * get_service(struct soap *soap, char * addressWsdl) {
	// return physical address of a service wsdl
	xmlDoc *doc = NULL;
	xmlNode *root = NULL;
	xmlNode *service = NULL;

	char * service_file;
	config_get_string(&service_file, "SERVICES_FILE");

	doc = xmlReadFile(service_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		fprintf(stderr, "genericFactory.c: deployment file %s doesn't exist",
				service_file);
		return NULL;
	}

	root = xmlDocGetRootElement(doc);
	service = root ->children;

	while (service) {
		if (strstr(xmlGetProp(service, "addressWsdl"), addressWsdl)) {
			return xmlGetProp(service, "path");
		}
		service = service->next;
	}

	xmlFreeDoc(doc);

	return NULL;

}

int http_get(struct soap *soap) {
	// implements HTTP GET method
	char *s = strchr(soap->path, '?');
	if (!s || strcmp(s, "?wsdl"))
		return SOAP_GET_METHOD;
	FILE * f = fopen(get_service(soap, soap->path), "rb");
	if (!f)
		return 404;
	soap->http_content = "text/xml";
	soap_response(soap, SOAP_FILE);
	for (;;) {
		int r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), f);
		if (!r)
			break;
		if (soap_send_raw(soap, soap->tmpbuf, r))
			break;
	}
	fclose(f);
	soap_end_send(soap);
	return SOAP_OK;
}

void *process_request(void *soap) {
	pthread_detach(pthread_self());
	soap_serve((struct soap*) soap);
	soap_destroy((struct soap*) soap);
	// dealloc C++ data
	soap_end((struct soap*) soap);
	// dealloc data and clean up
	soap_done((struct soap*) soap);
	// detach soap struct
	free(soap);
	return NULL;
}

int main(int argc, char **argv) {

	struct soap soap;
	soap_init(&soap);
	soap.fget = http_get;

	soap.send_timeout = 5;
	soap.recv_timeout = 5;
	soap.accept_timeout = 3600;
	// server stops after 1 hour of inactivity
	soap.max_keep_alive = 100;
	// max keep-alive sequence

	void *process_request(void*);
	struct soap *tsoap;
	pthread_t tid;
	SOAP_SOCKET m, s;

	int generic_factory_port;
	config_get_int(&generic_factory_port, "GENERIC_FACTORY_PORT");
	char * generic_factory_ip;
	config_get_string(&generic_factory_ip, "GENERIC_FACTORY_IP");

	m = soap_bind(&soap, NULL, generic_factory_port, 100);

	if (!soap_valid_socket(m)) {
		fprintf(stderr,
				"genericFactory.c: %s failed to start, port %d is busy\n",
				serviceName, generic_factory_port);
		exit(1);
	}

	service_set_uuid(&uuid);
	char * generic_factory_location;
	config_get_string(&generic_factory_location, "GENERIC_FACTORY_LOCATION");
	generic_factory_location = strcat(generic_factory_location, "?wsdl");
	char * awsm_home;
	config_get_string(&awsm_home, "AWSM_HOME");
	service_deploy(serviceName, generic_factory_location, strcat(awsm_home,
			"/build/genericFactory/GenericFactory.wsdl"), uuid);
	fprintf(stderr, "genericFactory.c: %s with id %s started\n", serviceName,
			uuid);

	for (;;) {
		s = soap_accept(&soap);
		if (!soap_valid_socket(s)) {
			if (soap.errnum) {
				soap_print_fault(&soap, stderr);
				exit(1);
			}
			fprintf(stderr, "genericFactory.c: server timed out\n");
			service_undeploy(serviceName, uuid);
			break;
		}
		fprintf(
				stderr,
				"[soap] genericFactory.c: request from IP %d.%d.%d.%d processed\n",
				(soap.ip >> 24) & 0xFF, (soap.ip >> 16) & 0xFF, (soap.ip >> 8)
						& 0xFF, soap.ip & 0xFF);
		tsoap = soap_copy(&soap);
		// make a safe copy
		if (!tsoap)
			break;
		pthread_create(&tid, NULL, (void*(*)(void*)) process_request,
				(void*) tsoap);
	}

	soap_done(&soap);
	// detach soap struct
	return 0;

}

int ns__adapt(struct soap *soap, char *app, char * appID, char *serviceID,
		char *target, char * storeWSDL, char **targetWSDL) {

	get_simple_name(&target);

	fprintf(
			stderr,
			"genericFactory.c: adapting service %s with id %s to interface %s\n",
			app, serviceID, target);

	struct timeval t_start, t_finish, t_elapsed;
	gettimeofday(&t_start, NULL);

	//start adapting
	service_undeploy(app, serviceID);
	char * awsm_home;
	config_get_string(&awsm_home, "AWSM_HOME");
	char * targetPath = soap_malloc(soap, 256);
	sprintf(targetPath, "%s/build/services/%s", awsm_home, target);

	//create services directory
	char * command = soap_malloc(soap, 1024);
	sprintf(command, "mkdir -p %s", targetPath);
	system(command);

	//compile with soapcpp2, produce stub, wsdl etc.
	sprintf(command, "%s/gsoap/soapcpp2 -c %s/src/%s.h -d%s> /dev/null 2>&1",
			awsm_home, awsm_home, target, targetPath);
	if (system(command)) {
		char *error = soap_malloc(soap, 1024);
		sprintf(error, "genericFactory.c: code for %s doesn't exist", target);
		return soap_sender_fault(soap, error, NULL);
	}

	sprintf(command, "gcc -I/usr/include/libxml2 -I%s/gsoap "
		"-o%s/%s %s/src/%s.c %s/soapC.c %s/soapServer.c %s/gsoap/stdsoap2.c "
		"-lxml2 -lz -lm -lpthread -luuid -ldl -lservice -lstate "
		"-L%s/build/lib -Wl,--rpath,%s/build/lib", awsm_home, targetPath,
			target, awsm_home, target, targetPath, targetPath, awsm_home,
			awsm_home, awsm_home, awsm_home);
	if (system(command)) {
		char *error = soap_malloc(soap, 1024);
		sprintf(error,
				"genericFactory.c: something goes wrong with compilation using gcc");
		return soap_sender_fault(soap, error, NULL);
	}

	sprintf(command, "%s/%s \"%s\" \"%s\" &", targetPath, target, app,
			serviceID);
	if (system(command)) {
		char *error = soap_malloc(soap, 1024);
		sprintf(error, "genericFactory.c: %s cannot be launched", target);
		return soap_sender_fault(soap, error, NULL);
	}

	gettimeofday(&t_finish, NULL);
	timeval_subtract(&t_elapsed, &t_finish, &t_start);
	fprintf(stderr, "genericFactory.c: adaption used %.f ms\n",
			t_elapsed.tv_sec * 1000 + t_elapsed.tv_usec / 1.e3);

	*targetWSDL = soap_malloc(soap, 1024);
	sprintf(*targetWSDL, "http://localhost:8085/%s/%s.%s?wsdl", app, target,
			serviceID);
	return SOAP_OK;
}
